/**********************************************************************
Copyright (c) 2012 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:
    ...
**********************************************************************/
package org.datanucleus.query.inmemory;

import java.io.Serializable;
import java.util.AbstractList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import org.datanucleus.api.ApiAdapter;
import org.datanucleus.store.query.QueryResult;
import org.datanucleus.util.Localiser;

/**
 * Simple wrapper to results generated by the in-memory query process suitable for use by JDO/JPA.
 */
public class InMemoryQueryResult extends AbstractList implements QueryResult, Serializable
{
    private static final long serialVersionUID = 9111768802939625736L;

    ApiAdapter api;

    List results = null;

    /** Whether the results are close. */
    protected boolean closed = false;

    public InMemoryQueryResult(List results, ApiAdapter api)
    {
        this.results = results;
        this.api = api;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.query.QueryResult#close()
     */
    public void close()
    {
        if (closed)
        {
            return;
        }

        closed = true;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.query.QueryResult#disconnect()
     */
    public void disconnect()
    {
        // Nothing to disconnect since wraps the in-memory results.
    }

    /* (non-Javadoc)
     * @see java.util.AbstractCollection#contains(java.lang.Object)
     */
    @Override
    public boolean contains(Object o)
    {
        assertIsOpen();
        return results.contains(o);
    }

    /* (non-Javadoc)
     * @see java.util.AbstractCollection#containsAll(java.util.Collection)
     */
    @Override
    public boolean containsAll(Collection c)
    {
        assertIsOpen();
        return results.containsAll(c);
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#get(int)
     */
    @Override
    public Object get(int index)
    {
        assertIsOpen();
        return results.get(index);
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#indexOf(java.lang.Object)
     */
    @Override
    public int indexOf(Object o)
    {
        assertIsOpen();
        return results.indexOf(o);
    }

    /* (non-Javadoc)
     * @see java.util.AbstractCollection#isEmpty()
     */
    @Override
    public boolean isEmpty()
    {
        assertIsOpen();
        return results.isEmpty();
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#lastIndexOf(java.lang.Object)
     */
    @Override
    public int lastIndexOf(Object o)
    {
        assertIsOpen();
        return results.lastIndexOf(o);
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#subList(int, int)
     */
    @Override
    public List subList(int fromIndex, int toIndex)
    {
        assertIsOpen();
        return results.subList(fromIndex, toIndex);
    }

    /* (non-Javadoc)
     * @see java.util.AbstractCollection#toArray()
     */
    @Override
    public Object[] toArray()
    {
        assertIsOpen();
        return results.toArray();
    }

    /* (non-Javadoc)
     * @see java.util.AbstractCollection#toArray(T[])
     */
    @Override
    public Object[] toArray(Object[] a)
    {
        assertIsOpen();
        return results.toArray(a);
    }

    /* (non-Javadoc)
     * @see java.util.AbstractCollection#size()
     */
    @Override
    public int size()
    {
        assertIsOpen();
        return results.size();
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#iterator()
     */
    @Override
    public Iterator iterator()
    {
        Iterator resultIter = results.iterator();
        return new ResultIterator(resultIter);
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#listIterator()
     */
    @Override
    public ListIterator listIterator()
    {
        ListIterator resultIter = results.listIterator();
        return new ResultIterator(resultIter);
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#listIterator(int)
     */
    @Override
    public ListIterator listIterator(int index)
    {
        ListIterator resultIter = results.listIterator(index);
        return new ResultIterator(resultIter);
    }

    private class ResultIterator implements ListIterator
    {
        Iterator resultIter = null;

        public ResultIterator(Iterator iter)
        {
            this.resultIter = iter;
        }

        public boolean hasNext()
        {
            if (closed)
            {
                return false;
            }
            return resultIter.hasNext();
        }

        public Object next()
        {
            if (closed)
            {
                throw new NoSuchElementException();
            }
            return resultIter.next();
        }

        public boolean hasPrevious()
        {
            if (closed)
            {
                return false;
            }
            return ((ListIterator)resultIter).hasPrevious();
        }

        public Object previous()
        {
            if (closed)
            {
                throw new NoSuchElementException();
            }
            return ((ListIterator)resultIter).previous();
        }

        public int nextIndex()
        {
            return ((ListIterator)resultIter).nextIndex();
        }

        public int previousIndex()
        {
            return ((ListIterator)resultIter).previousIndex();
        }

        public void remove()
        {
            throw new UnsupportedOperationException(Localiser.msg("052604"));
        }

        public void set(Object e)
        {
            throw new UnsupportedOperationException(Localiser.msg("052604"));
        }

        public void add(Object e)
        {
            throw new UnsupportedOperationException(Localiser.msg("052604"));
        }
    }

    /**
     * Internal method to throw an Exception if the ResultSet is open.
     */
    protected void assertIsOpen()
    {
        if (closed)
        {
            String msg = Localiser.msg("052600");
            throw api.getUserExceptionForException(msg, null);
        }
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#addAll(int, java.util.Collection)
     */
    @Override
    public boolean addAll(int index, Collection c)
    {
        throw new UnsupportedOperationException(Localiser.msg("052604"));
    }

    /* (non-Javadoc)
     * @see java.util.AbstractCollection#addAll(java.util.Collection)
     */
    @Override
    public boolean addAll(Collection c)
    {
        throw new UnsupportedOperationException(Localiser.msg("052604"));
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#add(java.lang.Object)
     */
    @Override
    public boolean add(Object e)
    {
        throw new UnsupportedOperationException(Localiser.msg("052604"));
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#add(int, java.lang.Object)
     */
    @Override
    public void add(int index, Object element)
    {
        throw new UnsupportedOperationException(Localiser.msg("052604"));
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#clear()
     */
    @Override
    public void clear()
    {
        throw new UnsupportedOperationException(Localiser.msg("052604"));
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#remove(int)
     */
    @Override
    public Object remove(int index)
    {
        throw new UnsupportedOperationException(Localiser.msg("052604"));
    }

    /* (non-Javadoc)
     * @see java.util.AbstractCollection#remove(java.lang.Object)
     */
    @Override
    public boolean remove(Object o)
    {
        throw new UnsupportedOperationException(Localiser.msg("052604"));
    }

    /* (non-Javadoc)
     * @see java.util.AbstractCollection#removeAll(java.util.Collection)
     */
    @Override
    public boolean removeAll(Collection c)
    {
        throw new UnsupportedOperationException(Localiser.msg("052604"));
    }

    /* (non-Javadoc)
     * @see java.util.AbstractCollection#retainAll(java.util.Collection)
     */
    @Override
    public boolean retainAll(Collection c)
    {
        throw new UnsupportedOperationException(Localiser.msg("052604"));
    }

    /* (non-Javadoc)
     * @see java.util.AbstractList#set(int, java.lang.Object)
     */
    @Override
    public Object set(int index, Object element)
    {
        throw new UnsupportedOperationException(Localiser.msg("052604"));
    }
}